package io.github.veigara.tcp.framework;

/**
 * @author : zhouhx
 * @date : 2025/6/6 13:26
 */

import io.github.veigara.tcp.framework.event.DeviceEventBus;
import io.github.veigara.tcp.framework.factory.DeviceComponentFactoryRegistry;
import io.github.veigara.tcp.framework.metrics.DeviceMetricsCollector;
import io.github.veigara.tcp.framework.model.DeviceConnection;
import io.github.veigara.tcp.framework.model.DeviceServerConfig;
import io.github.veigara.tcp.framework.model.LengthDecodeConfig;
import io.github.veigara.tcp.framework.protocol.DeviceChannelHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 设备服务器
 */
public class DeviceServer {
    private static final Logger logger = LoggerFactory.getLogger(DeviceServer.class);

    private final DeviceServerConfig config;
    private final DeviceComponentFactoryRegistry factoryRegistry;
    private final DeviceEventBus eventBus;
    private final DeviceMetricsCollector metricsCollector;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private Channel serverChannel;

    private final Map<String, DeviceConnection> deviceConnections = new ConcurrentHashMap<>();

    public DeviceServer(DeviceServerConfig config,
                        DeviceComponentFactoryRegistry factoryRegistry,
                        DeviceEventBus eventBus,
                        DeviceMetricsCollector metricsCollector) {
        this.config = config;
        this.factoryRegistry = factoryRegistry;
        this.eventBus = eventBus;
        this.metricsCollector = metricsCollector;
    }

    // 启动服务器
    public void start() {
        bossGroup = new NioEventLoopGroup(config.getNettyConfig().getBossThreads());
        workerGroup = new NioEventLoopGroup(config.getNettyConfig().getWorkerThreads());

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            LengthDecodeConfig lengthDecodeConfig = config.getLengthDecodeConfig();
                            // 根据配置添加长度解析器
                            if (lengthDecodeConfig.isUseLengthDecoder()) {
                                pipeline.addLast(
                                        new LengthFieldBasedFrameDecoder(
                                                lengthDecodeConfig.getMaxFrameLength(),
                                                lengthDecodeConfig.getLengthFieldOffset(),
                                                lengthDecodeConfig.getLengthFieldLength(),
                                                lengthDecodeConfig.getLengthAdjustment(),
                                                lengthDecodeConfig.getInitialBytesToStrip(),
                                                true
                                        )
                                );
                            }
                            pipeline.addLast(
                                    new IdleStateHandler(
                                            config.getNettyConfig().getReadTimeout(),
                                            config.getNettyConfig().getWriteTimeout(),
                                            config.getNettyConfig().getAllTimeout(),
                                            TimeUnit.SECONDS
                                    ),
                                    (ChannelHandler) config.getDecoderClass().getDeclaredConstructor().newInstance(),
                                    (ChannelHandler) config.getEncoderClass().getDeclaredConstructor().newInstance(),
                                    new DeviceChannelHandler(
                                            config.getDeviceType(),
                                            factoryRegistry,
                                            DeviceServer.this,
                                            eventBus,
                                            metricsCollector
                                    )
                            );
                        }
                    });

            serverChannel = b.bind(config.getPort()).sync().channel();
            logger.info("Device server started on port {} for {}",
                    config.getPort(), config.getDeviceType());
        } catch (Exception e) {
            logger.error("Failed to start server on port {}", config.getPort(), e);
            stop();
        }
    }

    // 停止服务器
    public void stop() {
        if (serverChannel != null) {
            serverChannel.close();
            serverChannel = null;
        }

        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
            workerGroup = null;
        }

        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
            bossGroup = null;
        }

        // 断开所有设备连接
        deviceConnections.values().forEach(DeviceConnection::disconnect);
        deviceConnections.clear();

        logger.info("Device server stopped on port {}", config.getPort());
    }

    // 添加设备连接
    public void addDeviceConnection(DeviceConnection connection) {
        deviceConnections.put(connection.getDeviceConnectionInfo().getDeviceId(), connection);
        metricsCollector.recordDeviceConnected(config.getDeviceType());
    }

    // 移除设备连接
    public void removeDeviceConnection(String deviceId) {
        deviceConnections.remove(deviceId);
        metricsCollector.recordDeviceDisconnected(config.getDeviceType());
    }

    // 获取设备连接
    public DeviceConnection getDeviceConnection(String deviceId) {
        return deviceConnections.get(deviceId);
    }

//    public void updateLastActiveTime(String deviceId) {
//        try {
//            String key = DEVICE_CONNECTION_KEY + deviceId;
//            String value = redisTemplate.opsForValue().get(key);
//            if (value != null) {
//                DeviceConnection connection = objectMapper.readValue(value, DeviceConnection.class);
//                connection.setLastActiveTime(new Date());
//                redisTemplate.opsForValue().set(key, objectMapper.writeValueAsString(connection), CONNECTION_TIMEOUT, TimeUnit.MINUTES);
//            }
//        } catch (Exception e) {
//            throw new RuntimeException("Failed to update last active time", e);
//        }
//    }
}